'''
Created on Jan 29, 2010

@author: Andrzej
'''
import serial
import telnetlib
import time
import re 
#import numpy as np
#import matplotlib.pyplot as plt

PROMPT_FOUND     = 1
PROMPT_NOT_FOUND = 0
PORT_OPEN        = 1
PORT_CLOSED      = 0
PORT_ERROR       = -1

TIMEOUT          = 2

import re
_ip_addr = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
        

class Arduino:
    """
    The Arduino interface object
    """
       
    debug = 3;
    Interface = None    

    def __init__(self, host='COM', port=7):
         
         if len(_ip_addr.findall(str(host))) == 1:             
             print "Address is IP"
             host = port
         else:
             print "Address is COM"
             host = 'COM'
             
        if host == 'COM':
            self.Interface = SerialInterface(port=port);
        else:
            self.Interface = TelnetInterface(host, port=2000);
        dbg(self, 2, "Arduino(host=%s, port=%d)" % (host, port))
        self.open()


    def  __unicode__(self):
        msg = 'Arduino'
        return msg
        
    def __del__(self):
        dbg(self, 2, "def __del__(self):")
        self.close()

    def open(self):
        dbg(self, 2, "def open(self):")
        self.Interface.open()

    def close(self):
        dbg(self, 2, "def close(self):")
        self.Interface.close()        
        return self.Interface.isOpen()
    
    def send(self, cmd):
        msg = 'def send(self, cmd="%s"):' % cmd
        dbg(self, 2, msg)
        self.Interface.send(cmd)
    
    def query(self, cmd, expected_text='', tag='',delay=0):
        dbg(self, 1, "def query(self,cmd=%s):" % cmd)
        if tag:
            expected_text = '</' + tag + '>'
        if self.prompt():
            self.send(cmd)
            time.sleep(delay)
            out   = self.read(expected_text)
            if tag:
                pattern = re.compile(r'(?:<{0}>)(.*)(?:</{0}>)'.format(tag), re.DOTALL)
                temp = pattern.findall(out)
                if len(temp)>0:
                    out = temp[0]
                else:
                    out = ''
            return out           
        else:
            dbg(self,1, "Empty query for: %s" % cmd)
            return ''
                   
    def read(self,expected_text=''):
        #dbg(self, 2, "def read(self):")
        response = self.Interface.read(expected_text)
        #dbg(self, 3, "Response: " + response)
        return response
    
    def prompt(self,attempt=1):
        
        max_attempt = 3
        prompt = self.Interface.GetPrompt()        
        if prompt == PROMPT_FOUND:
            return True
        elif prompt == PORT_ERROR:
            self.Interface.close()
            time.sleep(0.5)
            self.Interface.open()
            if attempt > max_attempt:
                return False
            else:
                attempt+=1
                self.prompt(attempt)
        else:
            if attempt > max_attempt:
                return False
            else:
                attempt+=1
                self.prompt(attempt)
        
    def SetServoPosition(self, pos, servo=1, delay=0):
        pos=pos + 250        
        cmd = 'duty %d %d' % (servo, pos)
        print cmd
        self.Ard.send(cmd)
        time.sleep(delay)

class TelnetInterface():
    '''
    classdocs
    '''
    host  = ''
    port  = 0
    debug = 0;
    _re_prompt = re.compile('(cmd>)')
    Tel  = telnetlib.Telnet();
    Tel.timeout = 2

    def __init__(self, host, port):
        self.host = host
        self.port = port       
        self.open()
    
    def __del__(self):
        dbg(self,1,"Deleting Interface")
        if not(self.Tel.sock):
            dbg(self, 2, "no open sockets")
        else:
            self.Tel.close()
    
    def __unicode__(self):
        return "TelnetInterface(%s:%d)" % (self.host, self.port)
   
    def isOpen(self):
        if self.Tel.sock == None:
            return PORT_CLOSED
        else:
            if self.Tel.sock == 0:
                return PORT_CLOSED
            else:
                return PORT_OPEN
        
    def GetPrompt(self):
        dbg(self,1,"def GetPrompt(self):")
        port_status = self.open()
        if port_status == PORT_OPEN:        
            try:
                self.Tel.write('\n')
                prompt_str = self.Tel.read_until('cmd>', timeout=0.2)
                if len(prompt_str) > 0:
                    dbg(self,2,"PROMPT_FOUND:")
                    dbg(self,2,prompt_str)
                    return PROMPT_FOUND
                else:
                    dbg(self,2,"PROMPT_NOT_FOUND")
                    return PORT_ERROR
            except:
                dbg(self,1,'Exception in "def GetPrompt(self):"')
                dbg(self,2,"PROMPT_NOT_FOUND")
                return PORT_ERROR
        elif port_status == PORT_ERROR:
            dbg(self,2,"PORT_ERROR")
            return PORT_ERROR                   
        else:
            dbg(self,2,"PROMPT_NOT_FOUND")
            return PORT_ERROR
            
    def open(self):
        if self.isOpen():
            return PORT_OPEN
        else:   
            dbg(self,1,"Open Telnet:" + self.host) 
            try:
                self.Tel.open(self.host, self.port, timeout=1)
                return PORT_OPEN
            except:
                print "Error occured while oppening Telnet Interface"
                return PORT_ERROR
    
    def close(self):
        if not self.isOpen():
            return
        else:   
            dbg(self,1, "Closing Telnet:" + self.host) 
            try:
                self.Tel.close()
            except:
                print "Error occured"
        
    def send(self, cmd):
        msg = 'def send(self, cmd="%s"):' % cmd
        dbg(self, 1, msg)
        
        if (cmd[-1] == "\n"):
            pass            
        else:
            msg = "adding CR to cmd"
            dbg(self, 2, msg)
            cmd += "\n"
          
        if self.open() == PORT_OPEN:            
            self.Tel.write(cmd)
            time.sleep(0.01)
        else:
            msg = "Serial port is not open"
            dbg(self, 2, msg)
        pass
        
    def read(self,expected_text=''):
        if not(self.Tel.sock):
            print "Cannot read, no active connection"
            response = ''
        else:
            try:
                if len(expected_text)>0:
                    response = self.Tel.read_until(expected_text, timeout=TIMEOUT)
                else:
                    response = self.Tel.read_very_eager()
            except(EOFError):
                response = ''
        dbg(self, 2, "Read   : " + response)
        return response

class SerialInterface():
   
    host  = 'COM'
    port  = 0
    debug = 0
    _re_prompt = re.compile('(cmd>)')
    Ser  = [];
    
    
    def __init__(self, port=1):
        
        self.port = port
        self.Ser = serial.Serial(port=port-1,baudrate=115200,timeout=1)        
                
    def __del__(self):
        dbg(self,1,"def __del__(self):")
        self.Ser.close()
        
    def __unicode__(self):
        return "SerialInterface"
            
    def isOpen(self):
        return self.Ser.isOpen()
    
    def GetPrompt(self):
        dbg(self,1,"def GetPrompt(self):")
        port_status = self.open()
        if port_status == PORT_OPEN:
            try:
                self.Ser.write('\n')
                #prompt_str = self.Tel.read_until('cmd>', timeout=0.1)
                time.sleep(0.05)
                prompt_str = self.Ser.read(self.Ser.inWaiting())
                if prompt_str.find('cmd>') > 0:
                    return PROMPT_FOUND
                else:
                    return PROMPT_NOT_FOUND
            except:
                dbg(self,1,'Error in def GetPrompt(self):')
                return PORT_ERROR
        elif port_status == PORT_ERROR:
            return PORT_ERROR                   
        else:
            return PROMPT_NOT_FOUND
    
    def open(self):
        dbg(self, 1, "def open(self):")
        if not(self.Ser.isOpen()):            
            try:
                self.Ser.open()
                dbg(self, 2, "Port %d is now open" % self.port)
                return PORT_OPEN
            except serial.SerialException:
                dbg(self, 0, "SerialException Cannot open port: %d " % self.port)
                return PORT_ERROR
            except:
                dbg(self, 0, "OtherException Cannot open port: %d" % self.port)
                return PORT_ERROR
        else:
            dbg(self, 2, "Port %d is already open" % self.port)
            return PORT_OPEN        
    
    def close(self):
        dbg(self, 1, "def close(self)")
        if self.Ser.isOpen():
            dbg(self, 3, "Closing port: %d " % self.port)
            self.Ser.close()
        else:
            dbg(self, 3, "Port: %d is already closed" % self.port)
            pass
       
    def send(self, cmd):
        msg = 'def send(self, cmd="%s"):' % cmd
        dbg(self, 1, msg)
        
        if (cmd[-1] == "\n"):
            pass            
        else:
            msg = "adding CR to cmd"
            dbg(self, 2, msg)
            cmd += "\n"
          
        if self.open() == PORT_OPEN:            
            self.Ser.write(cmd)
            time.sleep(0.01)
        else:
            msg = "Serial port is not open"
            dbg(self, 2, msg)
        pass
    
    def read(self, expected_text=''):
        #dbg(self, 1, "def read(self):")
        if self.open() == PORT_OPEN:
            try:
                TIMEOUT = 10
                to = time.clock()
                response = ''
                while time.clock()-to < TIMEOUT:
                    n = self.Ser.inWaiting()
                    if n > 0:
                        response += self.Ser.read(n)
                    if expected_text in response:
                        return response
                return response
            except:
                return ''
        else:
            dbg(self, 2, "Could not open the port")
            return ''

def dbg(obj, level, msg):
    if obj.debug >= level:
        print obj.__unicode__()+ ": " + "  "*level + msg
    else:
        pass
    
if __name__ == '__main__':
    print "================================"
    print "Arduino module test function"
    #A = Arduino('COM',7)
    A = Arduino('192.168.1.100',2000)
    A.debug = 3
    A.open()
    A.prompt()
    str = A.query('I\n')
    print str
    
    
    